package org.basis.enhance.async.init.processor;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.basis.enhance.async.init.executor.AsyncTaskExecutor;
import org.basis.enhance.async.init.registy.AsyncInitBeanRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.PriorityOrdered;
import org.springframework.lang.NonNull;

import java.lang.reflect.Method;
import java.util.concurrent.CountDownLatch;

/**
 * 异步初始化代理processor
 *
 * @author wenpan 2023/06/11 16:36
 */
public class AsyncProxyBeanPostProcessor implements BeanPostProcessor, ApplicationContextAware, PriorityOrdered {

    private final static Logger logger = LoggerFactory.getLogger(AsyncProxyBeanPostProcessor.class);

    private ApplicationContext applicationContext;

    /**
     * bean初始化方法调用之前执行
     */
    @Override
    public Object postProcessBeforeInitialization(@NonNull Object bean, String beanName) throws BeansException {
        String initMethodName = AsyncInitBeanRegistry.getAsyncInitMethodName(beanName);
        // 没有需要异步执行的init方法，直接返回原始bean
        if (initMethodName == null || initMethodName.length() == 0) {
            return bean;
        }
        ProxyFactory proxyFactory = new ProxyFactory();
        // 目标bean的Class
        proxyFactory.setTargetClass(bean.getClass());
        // 目标bean对象
        proxyFactory.setTarget(bean);
        AsyncInitializeBeanMethodInvoker methodInvoker = new AsyncInitializeBeanMethodInvoker(bean, initMethodName, beanName);
        // 切面
        proxyFactory.addAdvice(methodInvoker);
        return proxyFactory.getProxy();
    }

    @Override
    public Object postProcessAfterInitialization(@NonNull Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Override
    public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Override
    public int getOrder() {
        return PriorityOrdered.HIGHEST_PRECEDENCE;
    }

    /**
     * invoker
     */
    class AsyncInitializeBeanMethodInvoker implements MethodInterceptor {
        /**
         * 目标对象
         */
        private final Object targetObject;
        /**
         * 异步执行的方法名
         */
        private final String asyncMethodName;
        /**
         * bean名称
         */
        private final String beanName;
        private final CountDownLatch initCountDownLatch = new CountDownLatch(1);
        /**
         * 标记初始化方法是否在执行中
         */
        private volatile boolean isAsyncCalling = false;
        /**
         * 标记初始化方法是否执行完成
         */
        private volatile boolean isAsyncCalled = false;

        public AsyncInitializeBeanMethodInvoker(Object targetObject, String asyncMethodName, String beanName) {
            this.targetObject = targetObject;
            this.asyncMethodName = asyncMethodName;
            this.beanName = beanName;
        }

        @Override
        public Object invoke(@NonNull MethodInvocation invocation) throws Throwable {
            if (AsyncTaskExecutor.isStarted()) {
                // 如果spring容器已经启动完毕，则执行目标方法
                return invocation.getMethod().invoke(targetObject, invocation.getArguments());
            }
            Method method = invocation.getMethod();
            String methodName = method.getName();
            // 如果异步执行的方法还没提交过，则提交到线程池执行他
            if (!isAsyncCalled && methodName.equals(asyncMethodName)) {
                isAsyncCalled = true;
                isAsyncCalling = true;
                AsyncTaskExecutor.submitTask(applicationContext.getEnvironment(), () -> {
                    long startTime = System.currentTimeMillis();
                    try {
                        // 异步执行目标方法
                        invocation.getMethod().invoke(targetObject, invocation.getArguments());
                        logger.info("{}({}) {} method execute {} ms.",
                                targetObject.getClass().getName(), beanName, methodName, System.currentTimeMillis() - startTime);
                    } catch (Throwable e) {
                        throw new RuntimeException(e);
                    } finally {
                        isAsyncCalling = false;
                        // 锁减一，唤醒等待线程
                        initCountDownLatch.countDown();
                    }
                });
                return null;
            }
            // 启动过程中，如果异步方法还在执行中（比如：提交到了线程池，但可能还未被执行），但此时却调用了该对象的其他方法，则等待异步方法执行完成
            if (isAsyncCalling) {
                long startTime = System.currentTimeMillis();
                initCountDownLatch.await();
                logger.info("{}({}) {} method wait {} ms.",
                        targetObject.getClass().getName(), beanName, methodName, (System.currentTimeMillis() - startTime));
            }
            // 容器启动过程中，异步任务已经提交并且被线程池执行了，此时再调用代理类中的某个方法，则直接执行目标方法
            return invocation.getMethod().invoke(targetObject, invocation.getArguments());
        }
    }
}
